/*Scott Rogowski and Yipeng Huang*/
/*smr2167 yh2315*/

#include "stdarg.h"
#include "string.h"
#include "vpi_user.h"
#include "veriuser.h"
#include "params.h"

int get_options()
{

	// setup some default parameters
	default_params();

	// check for testbench command line parameters
	char *s;
	int seed = 0;

	s = (char *)mc_scan_plusargs("tb_seed+");
	if (s != NULL)
		seed = atoi(s);
	vpi_printf("Setting seed = %d\n", seed);
	srandom(seed);

	s = (char *)mc_scan_plusargs("tb_verbose+");
	if (s != NULL) {
		vpi_printf("Setting verbose = %s\n", s);
		params.verbose = atoi(s);
	}

	s = (char *)mc_scan_plusargs("tb_cfgfile+");
	if (s != NULL) {
		// open and parse the cfgfile
		vpi_printf("Setting cfgfile = %s\n", s);
		if (!read_params(read_params_file(s))) {
			vpi_printf
			    ("Error reading configuration file. Terminating.");
			return 0;
		}
	}

	if (params.auto_configure)
		auto_configure();
	print_params();

	if (params.verbose)
		vpi_printf("%9s : INFORMATION\n", "CYCLE");

	return 1;
}

void default_params()
{
	params.verbose = 1;
	params.auto_configure = 0;
	params.max_cycles = 10000;

	params.density_add = 0;
	params.density_addi = .50;
	params.density_lw = 0;
	params.density_sw = .50;
	params.density_beq = 0;
	params.density_jmp = 0;
	params.density_reset = .001;

	params.reg_read_index_mask = 0x01;
	params.reg_write_index_mask = 0x01;
	params.addi_mask = 0x0f;
	params.data_read_index_mask = 0x0f;
	params.data_write_index_mask = 0x0f;
	params.inst_read_index_mask = 0x0f;

}

int hexdig(char c)
{
	if (c == 'a')
		return 10;
	if (c == 'b')
		return 11;
	if (c == 'c')
		return 12;
	if (c == 'd')
		return 13;
	if (c == 'e')
		return 14;
	if (c == 'f')
		return 15;
	return (int)(c - '0');
}

int from5bithex(char *n)
{
	return hexdig(n[1]) + hexdig(n[0]) * (1 << 4);
}

int from32bithex(char *n)
{
	return hexdig(n[7]) + hexdig(n[6]) * (1 << 4) +
	    hexdig(n[5]) * (1 << 8) + hexdig(n[4]) * (1 << 12) +
	    hexdig(n[3]) * (1 << 16) + hexdig(n[1]) * (1 << 20) +
	    hexdig(n[1]) * (1 << 24) + hexdig(n[0]) * (1 << 28);
}

int dig2int(char c)
{
	return (int)(c - '0');
}

float float2dig(char *n)
{
	return dig2int(n[1]) * 0.1 + dig2int(n[2]) * 0.01;
}

char *readline(char *s)
{
	int i = 0;
	while (s[i] != '\n' && s[i] != 0)
		i++;
	char *line = (char *)malloc(sizeof(char) * (i + 1));
	int j;
	for (j = 0; j < i; j++)
		line[j] = s[j];
	line[i] = 0;
	return line;
}

char **splitlineintwo(char *line)
{
	char **result = (char **)malloc(2 * sizeof(char *));
	int i = 0;
	while (line[i] != ' ' && line[i] != 0)
		i++;
	result[0] = (char *)malloc(sizeof(char) * (i + 1));
	int j;
	for (j = 0; j < i; j++)
		result[0][j] = line[j];
	result[0][i] = 0;
	i++;
	int mark = i;
	while (line[i] != ' ' && line[i] != 0)
		i++;
	result[1] = (char *)malloc(sizeof(char) * (i + 1 - mark));
	for (j = mark; j < i; j++)
		result[1][j - mark] = line[j];
	result[1][i - mark] = 0;
	return result;
}

char *read_params_file(char *s)
{
	FILE *file = fopen(s, "r");
	char *result = (char *)malloc(2000 * sizeof(char));
	char *temp;
	int n = 0;
	while ((temp = fgets(result + n, 1000, file)) != NULL) {
		n += strlen(temp);
	}
	fclose(file);
	return result;
}

int read_params(char *s)
{
	if (s == NULL)
		return 0;
	while (1) {
		char *line = readline(s);
		int len = strlen(line);
		s += len + 1;
		if (len <= 1)
			break;
		char **pair = splitlineintwo(line);
		if (!strcmp(pair[0], "auto_configure")) {
			auto_configure();
		} else if (!strcmp(pair[0], "max_cycles")) {
			params.max_cycles = atoi(pair[1]);
		} else if (!strcmp(pair[0], "density_add")) {
			params.density_add = float2dig(pair[1]);
		} else if (!strcmp(pair[0], "density_addi")) {
			params.density_addi = float2dig(pair[1]);
		} else if (!strcmp(pair[0], "density_lw")) {
			params.density_lw = float2dig(pair[1]);
		} else if (!strcmp(pair[0], "density_sw")) {
			params.density_sw = float2dig(pair[1]);
		} else if (!strcmp(pair[0], "density_beq")) {
			params.density_beq = float2dig(pair[1]);
		} else if (!strcmp(pair[0], "density_jmp")) {
			params.density_jmp = float2dig(pair[1]);
		} else if (!strcmp(pair[0], "density_reset")) {
			params.density_reset = float2dig(pair[1]);
		} else if (!strcmp(pair[0], "reg_read_index_mask")) {
			params.reg_read_index_mask = from5bithex(pair[1]);
		} else if (!strcmp(pair[0], "reg_write_index_mask")) {
			params.reg_write_index_mask = from5bithex(pair[1]);
		} else if (!strcmp(pair[0], "addi_mask")) {
			params.addi_mask = from32bithex(pair[1]);
		} else if (!strcmp(pair[0], "data_read_index_mask")) {
			params.data_read_index_mask = from5bithex(pair[1]);
		} else if (!strcmp(pair[0], "data_write_index_mask")) {
			params.data_write_index_mask = from5bithex(pair[1]);
		} else if (!strcmp(pair[0], "inst_read_index_mask")) {
			params.inst_read_index_mask = from5bithex(pair[1]);
		}

	}
	return 1;
}

void auto_configure()
{
	params.max_cycles = 25;
	params.density_add = (rand() % 100) / 100;
	params.density_addi = (rand() % 100) / 100;
	params.density_lw = (rand() % 100) / 100;
	params.density_sw = (rand() % 100) / 100;
	params.density_beq = (rand() % 100) / 100;
	params.density_jmp = (rand() % 100) / 100;

	params.density_reset = (rand() % 10) / 100;	// constrain

	params.reg_read_index_mask = rand() % 32;
	params.reg_write_index_mask = rand() % 32;
	params.addi_mask = rand() % 65536;	// 2^16

	params.data_read_index_mask = rand() % 65536;	// 2^16
	params.data_write_index_mask = rand() % 65536;	// 2^16
	params.inst_read_index_mask = rand() % 65536;	// 2^16

}

void print_params()
{
	vpi_printf("Setting %s = %lld\n", "max_cycles", params.max_cycles);
	vpi_printf("Setting %s = %d\n", "auto_configure",
		   params.auto_configure);
	vpi_printf("Setting %s = %3.2f\n", "density_add", params.density_add);
	vpi_printf("Setting %s = %3.2f\n", "density_addi", params.density_addi);
	vpi_printf("Setting %s = %3.2f\n", "density_lw", params.density_lw);
	vpi_printf("Setting %s = %3.2f\n", "density_sw", params.density_sw);
	vpi_printf("Setting %s = %3.2f\n", "density_beq", params.density_beq);
	vpi_printf("Setting %s = %3.2f\n", "density_jmp", params.density_jmp);
	vpi_printf("Setting %s = %3.2f\n", "density_reset",
		   params.density_reset);
	vpi_printf("Setting %s = 0x%x\n", "reg_read_index_mask",
		   params.reg_read_index_mask);
	vpi_printf("Setting %s = 0x%x\n", "reg_write_index_mask",
		   params.reg_write_index_mask);
	vpi_printf("Setting %s = 0x%x\n", "addi_mask", params.addi_mask);
	vpi_printf("Setting %s = 0x%x\n", "data_read_index_mask",
		   params.data_read_index_mask);
	vpi_printf("Setting %s = 0x%x\n", "data_write_index_mask",
		   params.data_write_index_mask);
	vpi_printf("Setting %s = 0x%x\n", "inst_read_index_mask",
		   params.inst_read_index_mask);
}
